
#include <iostream>
#include <math.h>
#include <time.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glext.h>
#include <SDL/SDL.h>
#include "SDL/SDL_thread.h" 
#include "libraries/BM_init.h"
#include "libraries/BM_input.h"
#include "libraries/BM_time.h"
#include "libraries/BM_menu.h"
#include "libraries/BM_anitex.h"
#include "libraries/BM_level.h"
#include "libraries/BM_model.h"
#include "libraries/MD2/BM_md2.h"
#include "libraries/TGA/tga.h"
#include "libraries/MD2/BM_md2.h"
#include "libraries/BM_anitex.h"

#define SCREEN_WIDTH  800
#define SCREEN_HEIGHT 600
#define SCREEN_BPP     32

using namespace std;

#define FRAMEDELAY 50

void logError()
{
    int err = glGetError();
	if (err != GL_NO_ERROR)
	{
	    cout << "Error: " << err << std::endl;
	}
}

/* Logs a message into "log.txt"
 * @param const char * the error to output to the console
 * @param args the arguments
*/
void logMessage(const char *message, ...)
{
    char buffer[256];
    FILE * log;
    va_list args;

    va_start (args, *message);
    vsprintf (buffer,message, args);

    log = fopen("./log.txt", "w");
    fprintf(log, buffer);

    fclose(log);
    va_end (args);
}

void writeData(void *Data)
{
    FILE * log;

    log = fopen("./data.txt", "w");
    fwrite((void*)Data, 1, sizeof(Data), log);

    fclose(log);
}

BMModel model1, model2;
Model robot, box;

BMTex pcx_texture;

char Text[256]; // General purpose string

BMTex tex;

// load texture (TGA or PCX or BMP)
int loadTexture(char *TexName, GLuint TexHandle, int type)
{
    if (type == 1) // TGA
    {
        TGAImg Img; // Image loader

        // Load our Texture
        if(Img.Load(TexName)!=IMG_OK)
            return 0;

        glBindTexture(GL_TEXTURE_2D,TexHandle); // Set our Tex handle as current

        // Create the texture
        if(Img.GetBPP()==24)
            glTexImage2D(GL_TEXTURE_2D,0,3,Img.GetWidth(),Img.GetHeight(),0,GL_RGB,GL_UNSIGNED_BYTE,Img.GetImg());
        else if(Img.GetBPP()==32)
            glTexImage2D(GL_TEXTURE_2D,0,4,Img.GetWidth(),Img.GetHeight(),0,GL_RGBA,GL_UNSIGNED_BYTE,Img.GetImg());
        else
            return 0;

        // Specify filtering and edge actions
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP);
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP);
    }
    else if (type == 2) // PCX
    {
        // Load our Texture
        if(!tex.loadPCX(TexName))
        {
            return 0;
        }

        /*

        glBindTexture(GL_TEXTURE_2D,TexHandle); // Set our Tex handle as current

        // Create the texture
        if(tex.BPP==8) {
            glTexImage2D(GL_TEXTURE_2D,0,3,tex.Width,tex.Height,0,GL_RGB8,GL_UNSIGNED_BYTE, tex.pixels);
        }
        else
            return 0;

        // Specify filtering and edge actions
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP);
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP);*/
    }
    else if (type == 3) // BMP
    {
        // Load our Texture
        if(!tex.load(TexName))
        {
            return 0;
        }

        /*glBindTexture(GL_TEXTURE_2D,TexHandle); // Set our Tex handle as current

        // Create the texture
        if(tex.BPP==8) {
            glTexImage2D(GL_TEXTURE_2D,0,3,tex.Width,tex.Height,0,GL_RGB8,GL_UNSIGNED_BYTE, tex.pixels);
        }
        else
            return 0;

        // Specify filtering and edge actions
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP);
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP);*/
    }

    return 1;
}

GLfloat Ambient[]  = { 0.1f,  0.1f,  0.1f, 1.0f};	 // Ambient light value
GLfloat Diffuse[]  = { 1.0f,  1.0f,  1.0f, 1.0f};	 // Diffuse light value
GLfloat Position[] = {10.0f, 60.0f, 10.0f, 1.0f};	 // Light position

int loadMD2_Sample()
{
    model1.initModel(&robot, MOD_MD2); // init module as MD2 type // robot model
    model2.initModel(&box, MOD_MD2); // init module as MD2 type // box model

    int md2_error = 0;

    // Allocate all textures in one go
    glGenTextures(32,robot.md2.md2_model.Texture);
    glGenTextures(32,box.md2.md2_model.Texture);

    // Load our Robot Object
    if((md2_error = model1.loadModel("Obj/WalkMech.md2", &robot)) > 0)
    {
        cout << "Unable to load Object Robot! | Error: " << md2_error << std::endl;
        return -1;
    }

    // Load our box Object
    if((md2_error = model2.loadModel("Obj/box.md2", &box)) > 0)
    {
        cout << "Unable to load Object Box! | Error: " << md2_error << std::endl;
        return -1;
    }


    // Find out how many frames we have
    robot.md2.md2_model.Frames=robot.md2.md2_class.GetFrameCount(&robot.md2.md2_model);
    box.md2.md2_model.Frames=robot.md2.md2_class.GetFrameCount(&box.md2.md2_model);


    // Load a texture for our robot object
    strcpy(Text,"Obj/");
    strcat(Text,robot.md2.md2_class.GetTexName(&robot.md2.md2_model));

    if(loadTexture(Text,robot.md2.md2_model.Texture[0], 1))
        robot.md2.md2_class.SetTexture(robot.md2.md2_model.Texture[0], &robot.md2.md2_model);
    else
        logMessage("\nUnable to load Robot's texture! | Path: %s", Text);

    strcpy(Text,"Obj/");
    strcat(Text,box.md2.md2_class.GetTexName(&box.md2.md2_model));

    if(loadTexture(Text,box.md2.md2_model.Texture[0], 1))
        box.md2.md2_class.SetTexture(box.md2.md2_model.Texture[0], &box.md2.md2_model);
    else
        logMessage("\nUnable to load Box's texture! | Path: %s", Text);

    // Background color
    glClearColor(0.0f,0.0f,0.0f,1.0f);

    // Setup our screen
    glMatrixMode(GL_PROJECTION);
    glViewport(0,0,800,600);
    glLoadIdentity();
    glFrustum(-.5f,.5f,-.5f*(600.0f/800.0f),.5f*(600.0f/800.0f),1.0f,500.0f);
    glMatrixMode(GL_MODELVIEW);


    // Enable z-buffer
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_TEXTURE_2D);


    // Enable Lighting
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glLightfv(GL_LIGHT0, GL_AMBIENT, Ambient);	// Set the ambient lighting value for Light0
    glLightfv(GL_LIGHT0, GL_DIFFUSE, Diffuse);	// Set the diffuse lighting value for Light0

    // Set up TBM
    robot.md2.md2_model.Time1=robot.md2.md2_model.Time2=clock();
    robot.md2.md2_model.NextFrame=robot.md2.md2_model.Time1 + FRAMEDELAY;

    box.md2.md2_model.Time1=box.md2.md2_model.Time2=clock();
    box.md2.md2_model.NextFrame=box.md2.md2_model.Time1 + FRAMEDELAY;

    return 0;
}

int main( int argc, char **argv ){

    SDL_Surface *surface;
    int videoFlags;
    const SDL_VideoInfo *videoInfo;

	if( SDL_Init(SDL_INIT_EVERYTHING) < 0)
        return 1;

    videoInfo = SDL_GetVideoInfo( );

    if ( !videoInfo )
        return 1;

    videoFlags  = SDL_OPENGL;          // Enable OpenGL in SDL /
    videoFlags |= SDL_GL_DOUBLEBUFFER; // Enable double buffering
    videoFlags |= SDL_HWPALETTE;       // Store the palette in hardware

    if ( videoInfo->hw_available )
        videoFlags |= SDL_HWSURFACE;
    else
        videoFlags |= SDL_SWSURFACE;

    if ( videoInfo->blit_hw )
        videoFlags |= SDL_HWACCEL;

    SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );

    surface = SDL_SetVideoMode( SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, videoFlags );

    if ( !surface )
	    return 0;

    GLfloat ratio = ( GLfloat )SCREEN_WIDTH / ( GLfloat )SCREEN_HEIGHT;
    glViewport( 0, 0, ( GLsizei )SCREEN_WIDTH, ( GLsizei )SCREEN_HEIGHT );
    glMatrixMode( GL_PROJECTION );
    glLoadIdentity( );
    gluPerspective( 45.0f, ratio, 0.1f, 1000.0f );
    glMatrixMode( GL_MODELVIEW );
    glLoadIdentity( );

    glEnable( GL_TEXTURE_2D );
    glShadeModel( GL_SMOOTH );
    glClearColor( 0.0f, 0.0f, 1.0f, 1.0f );
    glClearDepth( 1.0f );
    glEnable( GL_DEPTH_TEST );
    glDepthFunc( GL_LEQUAL );
    glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST );

	SDL_ShowCursor(SDL_DISABLE);
	#ifdef WIN32
		SDL_WM_SetCaption( "Black Maze", NULL );
		//SDL_Surface* icon = SDL_LoadBMP("icon.bmp");
		//SDL_WM_SetIcon(icon, NULL);
	#endif

	//rendering_thread = SDL_CreateThread(Render, NULL );

	loadMD2_Sample();

	SDL_Event event;

	//logMessage("%s", glGetError());

	//level.load();
	while ( 1 ){
	    while ( SDL_PollEvent( &event ) ){
	        if( event.type == SDL_QUIT)
                return 0;
	    }

        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear The Screen And The Depth Buffer
        glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

        // Get ticks since last frame
        robot.md2.md2_model.Time2=clock();
        robot.md2.md2_model.Ticks=robot.md2.md2_model.Time2-robot.md2.md2_model.Time1;
        robot.md2.md2_model.Time1=robot.md2.md2_model.Time2;

        box.md2.md2_model.Time2=clock();
        box.md2.md2_model.Ticks=box.md2.md2_model.Time2-box.md2.md2_model.Time1;
        box.md2.md2_model.Time1=box.md2.md2_model.Time2;

        // Draw our Object
        model1.drawModel(&robot, 0.0f,-30.0f,-200.0f, robot.md2.md2_model.CurFrame, robot.md2.md2_model.Frames, Position);
        model2.drawModel(&box, 0.0f,15.0f,-400.0f, box.md2.md2_model.CurFrame, box.md2.md2_model.Frames, Position);

        // Advance the robot's frame counter
        if(robot.md2.md2_model.Time1>robot.md2.md2_model.NextFrame)
        {
            robot.md2.md2_model.CurFrame++;
            robot.md2.md2_model.NextFrame=robot.md2.md2_model.Time1 + FRAMEDELAY;

            if(robot.md2.md2_model.CurFrame>=robot.md2.md2_model.Frames)
                robot.md2.md2_model.CurFrame=0;
        }

        // Advance the box's frame counter
        if(box.md2.md2_model.Time1>box.md2.md2_model.NextFrame)
        {
            box.md2.md2_model.CurFrame++;
            box.md2.md2_model.NextFrame=box.md2.md2_model.Time1 + FRAMEDELAY;

            if(box.md2.md2_model.CurFrame>=box.md2.md2_model.Frames)
                box.md2.md2_model.CurFrame=0;
        }

        // Show our scene
        SDL_GL_SwapBuffers();

        // Rotate view for next frame // uncomment to rotate
        //robot.md2.md2_model.ViewRotate+=(robot.md2.md2_model.Ticks/50.0f);
        //box.md2.md2_model.ViewRotate+=(box.md2.md2_model.Ticks/50.0f);

        logError();
	}

	// Clean up textures
    glDeleteTextures(32,robot.md2.md2_model.Texture);
    glDeleteTextures(32,box.md2.md2_model.Texture);

    //SDL_KillThread(rendering_thread);

    //Free remaining data
    return 0;
}

